package com.ps.pspreview;

import java.util.Vector;


import com.ps.pspreview.imageview.PhotoViewAttacher;
import com.ps.pspreview.imageview.PhotoViewAttacher.OnMatrixChangedListener;
import com.ps.pspreview.imageview.PhotoViewAttacher.OnPhotoTapListener;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity implements ConfigureDialog.OnDoneListener{
	
	/** Worker thread to process incoming messages from Photoshop */
    MessageProcessor mMessageProcessor = null;
    
    /** the current document ID */
    private String mActiveDocumentID = new String("0");
    
    /** try to track how far behind I am in requests */
    private int mOutStandingImageRequests = 0;
    
    /** image view for this Activity */
    private ImageView mImageView = null;
    
    private Bitmap mBitmap = null;
    
    /** outstanding image request transaction IDs */
    private Vector<Integer> mOutStandingTransactionIDs = null;
    
    /** time I requested last image */
    long mRequestTime = System.currentTimeMillis();
    
    /** images can come in two types, we use strings */
    private static final String JPEG_TYPE_STR = new String("1");
    private static final String PIXMAP_TYPE_STR = new String("2");
    
    /** for image type we have Pixmap and JPEG */
    private static final int JPEG_TYPE = 1;
    private static final int PIXMAP_TYPE = 2;
    
    /** timing of last image, not correct when there is a backlog of requests */
    double mLastTime = 0.0;
    
    
    /** Name of the server to connect */
	private String mServerNameText = new String("192.168.1.101");
	
	/** Server password to use */
	private String mServerPasswordText = new String("123456");
	
	private PhotoViewAttacher mAttacher;
	
	private Toast mCurrentToast;
	
	static final String PHOTO_TAP_TOAST_STRING = "Photo Tap! X: %.2f %% Y:%.2f %%";
	
	
	 /** Messages that will come back to us from Photoshop when things change in Photoshop */
    private static final String CLOSED_DOCUMENT_STR = new String("closedDocument");
    private static final String NEW_DOCUMENT_VIEW_CREATED_STR = new String("newDocumentViewCreated");
    private static final String CURRENT_DOCUMENT_CHANGED_STR = new String("currentDocumentChanged");
    private static final String ACTIVE_VIEW_CHANGED_STR = new String("activeViewChanged");
    private static final String DOCUMENT_CHANGED_STR = new String("documentChanged");
    
    /** Some made up names I created to send messages to myself */
    private static final String SUBSCRIBE_SUCCESS_STR = new String("subscribeSuccess");
    private static final String DOCOMENT_NAMES_CHANGED_STR = new String("documentNamesChanged");
    private static final String ACTIVE_DOCUMENT_STR = new String("activeDocument");
    private static final String DOCUMENT_ID_STR = new String("documentID");

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		mOutStandingTransactionIDs = new Vector<Integer>();
//    	tryToConnect();
	
		setContentView(R.layout.activity_main);
		mImageView = (ImageView) findViewById(R.id.mImageView);
		Drawable bitmap = getResources().getDrawable(R.drawable.wallpaper);
		mImageView.setImageDrawable(bitmap);
		
		// The MAGIC happens here!
		mAttacher = new PhotoViewAttacher(mImageView);

		// Lets attach some listeners, not required though!
		mAttacher.setOnMatrixChangeListener(new MatrixChangeListener());
		mAttacher.setOnPhotoTapListener(new PhotoTapListener());
		
		checkIPConfigure();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// TODO Auto-generated method stub
		switch(item.getItemId()){
			case R.id.action_settings:
				new ConfigureDialog(this, this, mServerNameText, mServerPasswordText).show();
    			return true;
			default:
				return super.onOptionsItemSelected(item);
		}
		
	}
	
	/**
	 * 	check first ip configuration
	 */
	private void checkIPConfigure(){
		PreUtils mPre =  new PreUtils(this);
		mServerNameText = mPre.getValue(PreUtils.SERVER_NAME_KEY);
		mServerPasswordText = mPre.getValue(PreUtils.SERVER_PASSWORD_KEY);
		if(mServerNameText == null || mServerPasswordText == null){
			new ConfigureDialog(this, this, null, null).show();
		}
	}

	/**
     * Try to connect to Photoshop
     */
    private void tryToConnect() {
    	try {
    		mMessageProcessor = new MessageProcessor(this, mServerNameText, mServerPasswordText);
    		mMessageProcessor.start();
           	getActiveDocID();
           	setupEvents();
    	}
    	catch (Exception e) {
            showMessage( e.toString() );
            mMessageProcessor = null;
        } 
    }
	
    /**
     * Ask Photoshop to send us when a document is added, deleted, opened or changed.
     */
    private void setupEvents() {
    	subscribeToEvent(CLOSED_DOCUMENT_STR);
    	subscribeToEvent(NEW_DOCUMENT_VIEW_CREATED_STR);
    	subscribeToEvent(CURRENT_DOCUMENT_CHANGED_STR);
    	subscribeToEvent(ACTIVE_VIEW_CHANGED_STR);
    	subscribeToEvent(DOCUMENT_CHANGED_STR);
    }
    
    /**
     * Ask Photoshop to tell us when certain things happen.
     * 
     * NOTE: Make sure you set up the handler in the worker
     * thread to let the main thread know what is going on.
     * 
     * @param inEvent
     */
	private void subscribeToEvent(String inEvent) {
		String s = "var idNS = stringIDToTypeID( 'networkEventSubscribe' );";
		s += "var desc1 = new ActionDescriptor();";
		s += "desc1.putClass( stringIDToTypeID( 'eventIDAttr' ), stringIDToTypeID( '" + inEvent + "' ) );";
		s += "executeAction( idNS, desc1, DialogModes.NO );";
		s += "'" + SUBSCRIBE_SUCCESS_STR + "' + String.fromCharCode(13) + 'YES';";
		if (mMessageProcessor != null) {
			mMessageProcessor.sendJavaScript(s);
		}
	}
    
	/**
     * Ask Photoshop for the current image
     */
    private void getImage() {
    	getPhotoshopImage(mActiveDocumentID, false /* wantPixmap */, mImageView.getWidth(), mImageView.getHeight());
    	mOutStandingImageRequests++;
    	setTitle("Ps Images " + Integer.toString(mOutStandingImageRequests) + " " + Double.toString(mLastTime) + " " + Integer.toString(mOutStandingTransactionIDs.size()));
    	mRequestTime = System.currentTimeMillis();
    }

	/**
     * ID of the active document from Photoshop
     */
    private void setDocID(String inID) {
    	boolean getNewImage = false;
    	if (inID != mActiveDocumentID) {
    		getNewImage = true;
    	}
    	mActiveDocumentID = inID;
    	if (getNewImage) {
    		if (mActiveDocumentID.compareTo("0") != 0) {
    			getImage();
    		} else {
    			mImageView.setImageBitmap(null);
    		}
    	}
    }
	
	
	/**
     * Ask Photoshop for the current document ID
     */
    private void getActiveDocID() {
    	String s = "var docID = '0';";
    	s += "if (app.documents.length)";
    	s += "{";
    	s += "  docID = activeDocument.id;";
    	s += "}";
    	s += "'" + DOCUMENT_ID_STR + "' + String.fromCharCode(13) + docID;";
    	if (mMessageProcessor != null) {
    		mMessageProcessor.sendJavaScript(s);
    	}
    }
	
	/**
     * Called from the worker thread to tell us
     * something has happened with documents
     */
    public void setDocChanged() {
    	getActiveDocID();
    }
	
	
	/**
     * The thread will call us here with either a message from the
     * Photoshop JavaScript we sent or from something happening
     * in from Photoshop getting changed. This is why we have code
     * above that fakes the same messages as Photoshop will send us as
     * we really don't care where they originated from.
     * @param inBytes
     * @param inIndexer
     * @param inID

     */
    public void runFromThread(String inCommand, String inExtra, int inID) {
    	if (inCommand.compareTo(CLOSED_DOCUMENT_STR) == 0) {
        	setDocChanged();
        } else if (inCommand.compareTo(NEW_DOCUMENT_VIEW_CREATED_STR) == 0) {
        	setDocChanged();
        } else if (inCommand.compareTo(CURRENT_DOCUMENT_CHANGED_STR) == 0) {
        	setDocChanged();
        } else if (inCommand.compareTo(ACTIVE_VIEW_CHANGED_STR) == 0) {
        	setDocChanged();
        } else if (inCommand.compareTo(DOCOMENT_NAMES_CHANGED_STR) == 0) {
        	setDocChanged();
        } else if (inCommand.compareTo(DOCUMENT_ID_STR) == 0) {
        	setDocID(inExtra);
        } else if (inCommand.compareTo(DOCUMENT_CHANGED_STR) == 0) {
        	getImage();
        } else if (inCommand.compareTo(ACTIVE_DOCUMENT_STR) == 0) {
        	setDocChanged();
       	} else if (inCommand.compareTo(SUBSCRIBE_SUCCESS_STR) == 0) {
        	; // setTitle(Integer.toString(mSubscribeCount)); // this is good nothing to do for these
        } else {
        	showMessage(inCommand + ":" + inExtra);
        }
    }
    
    
    /**
     * Photoshop just sent me a image.
     * @param inBytes
     * @param inIndexer
     * @param inID
     */
    public void runFromThread(byte [] inBytes, int inIndexer, int inID) {
    	byte format = inBytes[inIndexer++];
    	if (JPEG_TYPE == format) {
    		mImageView.setImageBitmap(BitmapUtils.createBitmapFromJPEG(inBytes, inIndexer));
    		
    	} else if (PIXMAP_TYPE == format) {
    		mImageView.setImageBitmap(BitmapUtils.createBitmap(inBytes, inIndexer));
    	}
    	mImageView.invalidate();
    	mOutStandingImageRequests--;
    	mLastTime = ((double)System.currentTimeMillis() - (double)mRequestTime) / 1000.0;
    	setTitle("Ps Images " + Integer.toString(mOutStandingImageRequests) + " " + Double.toString(mLastTime) + " " + Integer.toString(mOutStandingTransactionIDs.size()));
    }
    
    /**
     * Request image data from Photoshop.
     * JPEG (1) or Pixmap (2) 
     * @param inImageID - unique ID of image given to me from Photoshop
     */
    private void getPhotoshopImage(String inImageID, boolean wantPixmap, int w, int h) {
    	boolean withIDs = true;
    	if (w <= 1 || h <= 1 || inImageID.length() == 0) { 
    		return; 
    	}
    	String s = "if (documents.length) {";
		s += "var idNS = stringIDToTypeID( 'sendDocumentThumbnailToNetworkClient' );";
		s += "var desc1 = new ActionDescriptor();";
		if (withIDs)
 			s += "desc1.putInteger( stringIDToTypeID( 'documentID' )," + inImageID + ");";
		s += "desc1.putInteger( stringIDToTypeID( 'width' )," + w + ");";
		s += "desc1.putInteger( stringIDToTypeID( 'height' )," + h + ");";
		s += "desc1.putInteger( stringIDToTypeID( 'format' )," + (wantPixmap ? PIXMAP_TYPE_STR : JPEG_TYPE_STR) + ");";
		s += "executeAction( idNS, desc1, DialogModes.NO );";
    	s += "'Image Request Sent';";
		s += "}";
    	if (mMessageProcessor != null) {
    		int id = mMessageProcessor.sendJavaScript(s);
    		dumpAllCurrentImageRequests();
    		mOutStandingTransactionIDs.add(id); 
    	}
    }
    
    /** a new image has been requested, dump all pending requests as they arrive */
    private void dumpAllCurrentImageRequests() {
		if (mOutStandingTransactionIDs.size() > 0) {
			for (int i = mOutStandingTransactionIDs.size() - 1; mOutStandingTransactionIDs.size() > 0; i++) {
				Integer id = mOutStandingTransactionIDs.elementAt(i);
				mMessageProcessor.dumpTransaction(id);
				mOutStandingTransactionIDs.removeElementAt(i);
			}
		}
	}
    
    /**
     * Show a short dialog of issues
     * @param inMessage - text to display
     */
    private void showMessage(String inMessage) {
    	Toast.makeText(this, inMessage, Toast.LENGTH_SHORT).show();
    }

	@Override
	public void optionsChanged(String server, String password) {
		// TODO Auto-generated method stub
		PreUtils pre = new PreUtils(this);
		pre.putValue(PreUtils.SERVER_NAME_KEY, server);
		pre.putValue(PreUtils.SERVER_PASSWORD_KEY, password);
		mServerNameText = server;
    	mServerPasswordText = password;
    	tryToConnect();
	}
	
	private class PhotoTapListener implements OnPhotoTapListener {

		@Override
		public void onPhotoTap(View view, float x, float y) {
			float xPercentage = x * 100f;
			float yPercentage = y * 100f;

			if (null != mCurrentToast) {
				mCurrentToast.cancel();
			}

			mCurrentToast = Toast.makeText(MainActivity.this,
					String.format(PHOTO_TAP_TOAST_STRING, xPercentage, yPercentage), Toast.LENGTH_SHORT);
			mCurrentToast.show();
		}
	}

	private class MatrixChangeListener implements OnMatrixChangedListener {

		@Override
		public void onMatrixChanged(RectF rect) {
			
		}
	}
}
